# -*- coding: utf-8 -*-

'''
Moduł zawierający definicję okna głównego programu.

'''

from PyQt4 import QtGui
from PyQt4 import QtCore

from ParamDialog import ParamDialog

##Okno główne programu.
class MainWindow(QtGui.QMainWindow):
    ##Konstruktor klasy
    #@param model obiekt SequenceAlignment, którego pracą steruje GUI.
    def __init__(self, model):
        super(MainWindow, self).__init__()
        self.model = model
        self.model.terminate()
        self.connect(self.model, QtCore.SIGNAL("started()"), self._blockMenu)
        self.connect(self.model, QtCore.SIGNAL("terminated()"), self._unblockMenu)
        self.connect(self.model, QtCore.SIGNAL("finished()"), self._readValuesAfterAlignment)
        self.connect(self.model, QtCore.SIGNAL("progressValue(int, int)"), self._updateProgressBar)

        self.nucleotideList = self.model.NUCLEOTIDE_LIST
        self.nucleotideColors = self.model.NUCLEOTIDE_COLORS
        
        self.setWindowTitle('TSC')
        
        self.mainWidget = QtGui.QWidget(self)
        self.setCentralWidget(self.mainWidget)
        self.mainLayout = QtGui.QHBoxLayout(self.mainWidget)
        
        self.nameView = QtGui.QTableWidget(self)
        self.seqView = QtGui.QTableWidget(self)
        self.seqView.setSelectionBehavior(QtGui.QAbstractItemView.SelectColumns)
        self.nameView.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
        self.seqView.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
        self.nameView.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Preferred)
        self.seqView.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred)
        self.nameView.verticalHeader().setResizeMode(QtGui.QHeaderView.Fixed)
        self.seqView.verticalHeader().setResizeMode(QtGui.QHeaderView.Fixed)
        self.seqView.horizontalHeader().setResizeMode(QtGui.QHeaderView.Fixed)
        self.mainLayout.addWidget(self.nameView)
        self.mainLayout.addWidget(self.seqView)
        
        self.progressBar = QtGui.QProgressBar(self)
        self.progressBar.setOrientation(QtCore.Qt.Vertical)
        self.mainLayout.addWidget(self.progressBar)
        
        self.paramDialog = ParamDialog(penaltyMax=model.PENALTY_MAX, matrixMinVal=model.MATRIX_MIN_VAL, matrixMaxVal=model.MATRIX_MAX_VAL, nucleotideList=model.NUCLEOTIDE_LIST, parent=self)
        
        self._createActions()
        self._createMenus()
        self._createStatusBar()
        
    
    def _wrongFileMessage(self):
        QtGui.QMessageBox.warning(self, 'Wrong file format.', 'Selected file does not contain 3 nucleotide sequences in FASTA format.', QtGui.QMessageBox.Ok, QtGui.QMessageBox.Ok)
                
    ##Funkcja wywoływana po wybraniu z menu otwarcia pliku z sekwencjami
    def _openSeqFile(self):
        fileName = QtGui.QFileDialog.getOpenFileName(self, 'Open sequences file', './sequences', "FAS format (*.fas);;All files (*)")
        if fileName != QtCore.QString():
            nameSeqList, columnsList = self.model.readSequences(fileName)
            if nameSeqList is not None:
                namesList = []
                sequencesList = []
                for elem in nameSeqList:
                    namesList.append(elem[0])
                    sequencesList.append(elem[1])
                self._loadSequencesView(sequencesList, columnsList, namesList)
                self.saveAlignment.setEnabled(False)
                #print 'Memory usage after loading sequences file in bytes: ' + str(resource.getrusage(resource.RUSAGE_SELF)[2]*resource.getpagesize() / 1000000.0)
            else:
                self._wrongFileMessage()
    
    ##Funkcja wywoływana po wybraniu z menu zapisu pliku z wyrównanymi sekwencjami
    def _saveFile(self):
        fileName = QtGui.QFileDialog.getSaveFileName(self, 'Open sequences file', './sequences', "Clustal W multiple sequence alignment(*.aln);;All files (*)")
        if fileName != QtCore.QString():
            if self.model.saveAlignment(fileName) != None:
                pass
            else:
                QtGui.QMessageBox.warning(self, 'Unable to save file.', 'Cannot save sequences alignment in file ' + fileName, QtGui.QMessageBox.Ok, QtGui.QMessageBox.Ok)
                
    ##Funkcja wywoływana po wybraniu obliczenia dopasowanych sekwencji z pliku
    def _calcAlignmentFromFile(self):
        fileName = QtGui.QFileDialog.getOpenFileName(self, 'Open alignment file', './sequences', "Clustal W multiple sequence alignment(*.aln);;All files (*)")
        if fileName != QtCore.QString():
            alignmentVal = self.model.readAlignment(fileName)
            if alignmentVal is not None:
                QtGui.QMessageBox.information(self, 'Alignment. ', 'Alignment value: ' + str(alignmentVal), QtGui.QMessageBox.Ok)
            else:
                QtGui.QMessageBox.warning(self, 'Wrong file format.', 'Selected file does not contain 3 aligned sequences in Clustal W format.', QtGui.QMessageBox.Ok, QtGui.QMessageBox.Ok)
                
    ##Funkcja powodująca wypełnienie do tabeli nazw sekwencji i sekwencji
    def _loadSequencesView(self, sequencesList, columnsList, namesList=None):
       
        #ustawienie nowej liczby wierszy oraz nagłówka
        if namesList is not None:
            self.nameView.clear()
            self.nameView.setRowCount(len(namesList))
            self.nameView.setColumnCount(1)
            self.nameView.setHorizontalHeaderItem(0, QtGui.QTableWidgetItem('Sequence name'))
            
        self.seqView.clear()        
        self.seqView.setRowCount(len(sequencesList))
        self.seqView.setColumnCount(0)
        
        #wstawienie nowych nazw sekwencji
        if namesList is not None:
            i = 0
            for elem in namesList:
                self.nameView.setItem(i, 0, QtGui.QTableWidgetItem(elem))
                i = i+1
            
        #wstawienie nowych sekwencji do drugiej tabeli
        i=0
        for elem in sequencesList:
            self.seqView.setColumnCount(max(len(elem), self.seqView.columnCount()))
            j=0
            for letter in elem:
                item = QtGui.QTableWidgetItem(letter)
                item.setTextAlignment(QtCore.Qt.AlignCenter)
                idx = self.nucleotideList.index(letter)
                if idx >= 0:
                    item.setBackgroundColor(self.nucleotideColors[idx])
                self.seqView.setItem(i, j, item)
                self.seqView.setHorizontalHeaderItem(j, QtGui.QTableWidgetItem(columnsList[j]))
                j=j+1
            i=i+1
            
        self.nameView.resizeColumnsToContents()
        self.seqView.resizeColumnsToContents()
        self.nameView.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch)
        self.progressBar.setValue(0)
    
    ##Funkcja ustawiająca w dialogu z parametry odczytane z modelu
    def _setParams(self):
        (penaltyVal, matrixNum) = self.model.getParams()

        self.paramDialog.setParams(penaltyVal, matrixNum)        
        
        ret = self.paramDialog.exec_()
        if ret == QtGui.QDialog.Accepted:
            (penaltyVal, matrixNum, matrix) = self.paramDialog.getParams()
            self.model.setParams(penaltyVal, matrixNum, matrix)
        elif ret == QtGui.QDialog.Rejected:
            pass

    ##Funkcja wywoływana po wybraniu z menu rozpoczęcia operacji dopasowywania sekwencji
    def _alignSequences(self):
#         seq, alignment = self.model._alignSequences()
        self.model.start()
        
    ##Funkcja odczytująca dopasowane sekwencje po zakończeniu dopasowywania
    def _readValuesAfterAlignment(self):    
        if self.model.ress1:
            sequencesList = []
            sequencesList.append(self.model.ress1)
            sequencesList.append(self.model.ress2)
            sequencesList.append(self.model.ress3)
            columnsList = self.model.ressColumns
            alignment = self.model.alignmentValue
            self._loadSequencesView(sequencesList, columnsList)
            message = "Alignment value: " + str(alignment)
            QtGui.QMessageBox.information(self, 'Alignment', QtCore.QString(message), QtGui.QMessageBox.Ok)
            self.statusBar().showMessage(self.tr(message))
            self.saveAlignment.setEnabled(True)
        self._unblockMenu()
        
    def _blockMenu(self):
        self.loadSequences.setEnabled(False)
        self.doAlignment.setEnabled(False)
        self.stopAlignment.setEnabled(True)
        self.progressBar.setVisible(True)
        
    def _unblockMenu(self):
        self.loadSequences.setEnabled(True)
        self.doAlignment.setEnabled(True)
        self.stopAlignment.setEnabled(False)
        self.progressBar.setVisible(False)
        
    def _updateProgressBar(self, val, maximum):
        if val==0:
            self.progressBar.setMaximum(maximum)
        self.progressBar.setValue(val)
    
    def _about(self):
        QtGui.QMessageBox.about(self, self.tr("TSC"), self.tr("Three Sequence Comparison tool.\n"))
    
    def _createActions(self):
        self.exitAct = QtGui.QAction(self.tr("E&xit"), self)
        self.exitAct.setShortcut(self.tr("Ctrl+Q"))
        self.exitAct.setStatusTip(self.tr("Exit the application"))
        self.connect(self.exitAct, QtCore.SIGNAL("triggered()"), self, QtCore.SLOT("close()"))
        
        self.aboutAct = QtGui.QAction(self.tr("&About"), self)
        self.aboutAct.setStatusTip(self.tr("Show the application's About box"))
        self.connect(self.aboutAct, QtCore.SIGNAL("triggered()"), self._about)
    
        self.aboutQtAct = QtGui.QAction(self.tr("About &Qt"), self)
        self.aboutQtAct.setStatusTip(self.tr("Show the Qt library's About box"))
        self.connect(self.aboutQtAct, QtCore.SIGNAL("triggered()"), QtGui.qApp, QtCore.SLOT("aboutQt()"))
        
        self.loadSequences = QtGui.QAction(self.tr("&Load sequences"), self)
        self.loadSequences.setShortcut(self.tr("Ctrl+O"))
        self.loadSequences.setStatusTip(self.tr("Open file with sequences to compare"))
        self.connect(self.loadSequences, QtCore.SIGNAL("triggered()"), self._openSeqFile)
        
        self.loadAlignment = QtGui.QAction(self.tr("&Calculate alignment"), self)
        self.loadAlignment.setShortcut(self.tr("Ctrl+L"))
        self.loadAlignment.setStatusTip(self.tr("Calculate alignment from .aln file"))
        self.connect(self.loadAlignment, QtCore.SIGNAL("triggered()"), self._calcAlignmentFromFile)
        
        self.saveAlignment = QtGui.QAction(self.tr("&Save alignment"), self)
        self.saveAlignment.setShortcut(self.tr("Ctrl+S"))
        self.saveAlignment.setStatusTip(self.tr("Save sequences alignment in CLUSTAL format"))
        self.saveAlignment.setEnabled(False)
        self.connect(self.saveAlignment, QtCore.SIGNAL("triggered()"), self._saveFile)
        
        self.doAlignment = QtGui.QAction(self.tr("Do ali&gnment"), self)
        self.doAlignment.setShortcut(self.tr("Ctrl+G"))
        self.doAlignment.setStatusTip(self.tr("Do sequences alignment"))
        self.connect(self.doAlignment, QtCore.SIGNAL("triggered()"), self._alignSequences)
        
        self.alignmentParams = QtGui.QAction(self.tr("Alignment &params"), self)
        self.alignmentParams.setShortcut(self.tr("Ctrl+P"))
        self.alignmentParams.setStatusTip(self.tr("Set sequences alignment parameters"))
        self.connect(self.alignmentParams, QtCore.SIGNAL("triggered()"), self._setParams)
        
        self.stopAlignment = QtGui.QAction(self.tr("&Cancel alignment"), self)
        self.stopAlignment.setShortcut(self.tr("Ctrl+C"))
        self.stopAlignment.setStatusTip(self.tr("Cancel alignment processing"))
        self.connect(self.stopAlignment, QtCore.SIGNAL("triggered()"), self.model.stopProcessing)
        
    def _createMenus(self):
        self.fileMenu = self.menuBar().addMenu(self.tr("&File"))
        self.fileMenu.addAction(self.loadSequences)
        self.fileMenu.addAction(self.saveAlignment)
        self.fileMenu.addAction(self.exitAct)
        
        self.alignmentMenu = self.menuBar().addMenu(self.tr("&Alignment"))
        self.alignmentMenu.addAction(self.alignmentParams)
        self.alignmentMenu.addAction(self.doAlignment)
        self.alignmentMenu.addAction(self.stopAlignment)
        self.alignmentMenu.addAction(self.loadAlignment)
        
        self._unblockMenu()
        
        self.helpMenu = self.menuBar().addMenu(self.tr("&Help"))
        self.helpMenu.addAction(self.aboutAct)
        self.helpMenu.addAction(self.aboutQtAct)
    
    def _createStatusBar(self):
        sb = QtGui.QStatusBar()
        sb.setFixedHeight(18)
        self.setStatusBar(sb)
        self.statusBar().showMessage(self.tr("Ready"))
        
    ##Redefinicja metody obsługującej przyjście zdarzenia zamknięcia.
    def closeEvent(self, event):
        quit_msg = "Are you sure you want to exit the program?"
        reply = QtGui.QMessageBox.question(self, 'Message', 
                         quit_msg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
    
        if reply == QtGui.QMessageBox.Yes:
            self.model.terminate()
            event.accept()
        else:
            event.ignore()
            